Conversation
- Replace docs/user/*.md links with lip-sigma.vercel.app equivalents - Convert docs/index.astro to index.mdx so markdown renders correctly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements LIP 2.1.0 proposal: callers stream symbols ranked by relevance
to a cursor position and stop reading when their prompt budget is full,
instead of fetching top-k and locally truncating.
- ClientMessage::StreamContext { file_uri, cursor_position, max_tokens, model? }
- ServerMessage::SymbolInfo { symbol_info, relevance_score, token_cost }
- ServerMessage::EndStream { reason, emitted, total_candidates, error? }
with EndStreamReason = budget_reached | exhausted | error
- Daemon writes one frame at a time; back-pressure throttles ranking,
BrokenPipe aborts the walk on client disconnect.
- Relevance order (spec §2.3): cursor symbol → callers (blast-radius CPG)
→ callees → related types.
- Conservative chars÷4 + 8 token-cost estimate per spec §2.4.
- protocol_version bumped 1 → 2 in HandshakeResult.
- New `lip stream-context <uri> <line:col> --max-tokens N` CLI subcommand.
- Integration tests cover zero-budget, cursor out-of-range, and v2 handshake.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: v2.1.0 — stream_context (token-budgeted RAG context streaming)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes the gap left by EmbeddingBatch (URI-only) and QueryNearestByText
(embeds internally but discards the vector). Callers re-ranking with
their own scoring — centroid arithmetic, federated nearest-neighbour,
lexical-then-semantic re-rank — get the embedding directly instead of
having to build a centroid out of nearest-neighbour seeds.
- ClientMessage::EmbedText { text, model? }
- ServerMessage::EmbedTextResult { vector, embedding_model }
- Handler reuses EmbeddingClient::embed_texts; returns the model
actually used (after any override).
- Rejected from Batch / BatchQuery (async HTTP, like other embedding ops).
- Round-trip + integration tests; CHANGELOG entry.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: embed_text — unary text-to-vector embedding
- HandshakeResult gains supported_messages: Vec<String> listing every
ClientMessage type tag this daemon understands. Clients can probe for
individual features (stream_context, embed_text) without comparing
protocol_version integers. Field is #[serde(default)] so older daemons
yield an empty vector.
- ServerMessage::UnknownMessage { message_type, supported } — when the
client sends a well-formed envelope whose type tag is unknown, the
daemon replies with UnknownMessage (carrying the tag + supported list)
and keeps the socket open, instead of closing after a generic parse
Error. Lets forward-compatible clients downgrade gracefully.
- Added integration tests for both behaviors; run loop inspects the
parse error text for "unknown variant" to distinguish recoverable
unknown-tag cases from malformed JSON.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(2.1): capability discovery + graceful unknown-variant handling
- Added `ErrorCode` enum (unknown_message_type, unknown_model, cursor_out_of_range, index_locked, internal) and `code: ErrorCode` field on `ServerMessage::Error`. Clients can now branch on a stable category instead of string-matching `message`. - `#[serde(default)]` keeps the wire backwards-compatible: older daemons deserialize as `ErrorCode::Internal`. - Classified all 10 "embedding endpoint not configured / no cached embedding for URI" sites as `unknown_model`; everything else defaults to `internal`. Integration test asserts the UnknownModel code on embed_text when LIP_EMBEDDING_URL is unset. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…odel (#12) QueryExpansion embedded the query with the requested model but then ranked candidates across all stored symbol embeddings regardless of which model produced them. Cross-model cosine scores are meaningless, so the returned "expansion terms" were noise whenever the index held mixed-model vectors. - Added `model_filter: Option<&str>` parameter to `LipDatabase::nearest_symbol_by_vector`. When `Some(m)`, only symbols whose stored embedding was produced by `m` are scored. - QueryExpansion handler captures the actual model returned by `embed_texts` (previously discarded) and passes it as the filter, guaranteeing query-vector and candidate-vectors share a model. - SimilarSymbols (resolves from a URI's own cached embedding) keeps the old unfiltered behavior by passing `None`. - Consolidated v2.1 CHANGELOG into one heading per user request. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
#13) Pre-release polish for 2.1.0. Once the tag ships, ErrorCode becomes load-bearing with its current meaning — splitting the conflations now is free; splitting them later is a breaking contract change. Error codes split: - `EmbeddingNotConfigured` — daemon has no LIP_EMBEDDING_URL - `NoEmbedding` — URI not yet embedded; retry after batch - `InvalidRequest` — client-side misuse (nested Batch, StreamContext inside Batch) Previously all three collapsed into `UnknownModel` / `Internal`, so clients could not tell "retry after embedding" from "model broken" from "my request shape is wrong." Drift guard: - `ClientMessage::variant_tag` is an exhaustive-match method; adding a new variant breaks compilation until acknowledged. The paired `supported_messages_covers_all_variants` test then enforces that the new tag also appears in the handshake capability list. Also: - benches/framing.rs lost the `code` field on ServerMessage::Error since it was added to the struct; repaired so `cargo bench` compiles. - CHANGELOG 2.1.0 reformatted with v2.0-style subsection headers and corrected to reflect the new, non-conflated error codes. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Closes the silent-staleness footgun on Tier 3 (SCIP) imports. The
daemon previously had no way to report *what* produced imported
symbols or *when* they landed, so stale Tier 3 data returned
confidently-wrong results with no observable signal.
New wire surface (additive, backwards-compatible):
- `Tier3Source { source_id, tool_name, tool_version, project_root,
imported_at_ms }` — provenance record.
- `ClientMessage::RegisterTier3Source { source }` — idempotent
registration; re-registering the same `source_id` refreshes
`imported_at_ms` in place. Ack'd with `DeltaAck`. Rejected inside
`BatchQuery` (mutation).
- `IndexStatusResult.tier3_sources: Vec<Tier3Source>` — sorted by
`source_id`. `#[serde(default)]`, so older daemons yield an empty
list to newer clients.
CLI wiring:
- `lip import --push-to-daemon` now extracts SCIP `Metadata.tool_info`
+ `project_root` and registers before streaming deltas. `source_id`
= `sha256(tool_name + ":" + project_root)` so re-imports of the
same source collapse to one entry.
- `lip-cli mcp` index-status text output now surfaces the tier3 list.
The daemon deliberately does no staleness detection. This is a
provenance primitive, not a policy: clients decide what "stale"
means (time threshold, commit drift vs. HEAD, etc.). The 2.1 goal
is visibility, not auto-reindex.
Tests: +4 (round-trip, missing-field fallback, sort order, re-reg
overwrite). Drift-guard sample list updated for the new variant.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ephemeral or test imports can skip `RegisterTier3Source` so they don't pollute a long-lived daemon's `tier3_sources` list. No effect on the default EventStream-JSON output path, where provenance was never sent anyway. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
) Ship-blockers flagged in the 2.1.0 review. **1. ErrorCode::UnknownModel is now actually reachable.** The embedding HTTP client previously collapsed every endpoint error into a generic anyhow::Error, forcing the session layer to tag all HTTP failures as ErrorCode::Internal. The UnknownModel code was defined and documented but unreachable — exactly the conflation the error-code split was meant to end. New `EmbedError` enum with four variants: UnknownModel, Transport, Protocol, Http. Classification lives in `daemon/embedding.rs::classify_http_error`: - 404 → UnknownModel (OpenAI/Ollama/most compatible backends) - 4xx with `model_not_found`, `"unknown model"`, or `"model … not found/invalid/unsupported"` in the body → UnknownModel - Auth (401), rate-limit (429), and 5xx → Http → Internal at the wire - Parse / count-mismatch → Protocol → Internal Conservative on purpose — mentioning "model" alone (e.g. in a token payload or a parameter error) does not flip it to UnknownModel. Session.rs routes classification via `embed_error_response(e)` at all 10 call sites, replacing the hand-rolled `format!("embedding failed: {e}")` + `ErrorCode::Internal` blocks. **2. QueryExpansion handler contract pinned by a db-level test.** Extracted the post-embedding ranking into `LipDatabase::query_expansion_terms(query_vec, actual_model, top_k)`. The session handler is now a single call, and the db method is covered by `query_expansion_terms_rejects_cross_model_scoring` which puts a matching-model symbol and a cross-model symbol at identical vectors and asserts only the matching-model term appears. A regression that silently drops the model filter would flip the assertion. Tests: +7 classifier cases, +1 handler-contract test (284 lib tests pass; was 276). Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md # Cargo.lock # Cargo.toml # bindings/rust/benches/framing.rs # bindings/rust/tests/integration.rs
FSEvents / inotify latency varies under load; a fixed 3s sleep was marginal on slow runners. Poll up to 15s with 100ms ticks instead — fast in the happy case, robust when the runner is loaded.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release v2.1.0 — Streaming context + forward-compat primitives
Merges 13 commits from
developintomain, plus the necessary conflict resolution for thelip → lip-corecrate rename that landed on main in v2.0.1 (#8). This is a real merge commit (not squash) so per-feature PR history (#11, #12, #13, #14, #15, #16) is preserved on main.Conflicts resolved
Cargo.toml— kept main's richer crates.io metadata (rust-version,documentation,nyxcore.cloudemail,lip-sigma.vercel.apphomepage) + bumpedversionto2.1.0.bindings/rust/benches/framing.rs,bindings/rust/tests/integration.rs— ported develop's newErrorCodeimports + expanded tests tolip_core::namespace (previouslylip::).tools/lip-cli/Cargo.toml,tools/lip-registry/Cargo.toml— bumped inner-workspacelip-coredep version2.0.1 → 2.1.0.CHANGELOG.md— placed 2.1.0 above the existing 2.0.1 entry.Incidental test fix
embed_text_without_endpoint_returns_errorexpected pre-splitUnknownModelfor unconfigured daemon. Post-split (shipped in feat(2.1): structured error codes on Error responses #11/fix(2.1): wire ErrorCode::UnknownModel + pin QueryExpansion contract #16), unconfigured daemons return the more preciseEmbeddingNotConfigured. Assertion updated to match.Incidental CI stability fix
daemon::watcher::tests::watcher_detects_file_changereplaced a fixed 3ssleepwith a 15s poll. The fixed sleep was marginal on slow CI runners and locally flaky. No behavior change — same assertion, just non-fragile timing.Highlights (full detail in CHANGELOG.md)
StreamContextwire message (§9.2), token-budgeted,EndStreamterminator, relevance tiers.protocol_versionbumped1 → 2. Newlip stream-contextsubcommand.EmbedText;RegisterTier3Source+IndexStatusResult.tier3_sourcesfor SCIP import provenance;lip import --no-provenanceopt-out.HandshakeResult.supported_messages; gracefulUnknownMessagereply keeping socket open;ServerMessage::Error { code: ErrorCode }with 8 distinct codes (UnknownModelwired via HTTP error classification indaemon/embedding.rs::classify_http_error).ClientMessage::variant_tag+ paired exhaustive-match test prevents capability-list drift.QueryExpansionhonors caller's model pin; handler contract pinned at db layer viaquery_expansion_terms_rejects_cross_model_scoring.Tests
cargo test --lib→ 277 passedcargo test --test integration→ 12 passedcargo testfull suite → all greenTest plan
cargo test --libcargo test --test integrationcargo check --all-targetsclean🤖 Generated with Claude Code